\subsection{Windows NT: Критические секции}
\myindex{Windows}

\label{critical_sections}

Критические секции в любой \ac{OS} очень важны в мультитредовой среде, используются в основном
для обеспечения гарантии что только один тред будет иметь доступ к данным в один момент времени,
блокируя остальные треды и прерывания.


\par
Вот как объявлена структура \TT{CRITICAL\_SECTION} 
объявлена в линейке OS \gls{Windows NT}:

\begin{lstlisting}[caption=(Windows Research Kernel v1.2) public/sdk/inc/nturtl.h,style=customc]
typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
\end{lstlisting}

Вот как работает функция EnterCriticalSection():

\myindex{x86!\Instructions!LOCK}
\begin{lstlisting}[caption=Windows 2008/ntdll.dll/x86 (begin),style=customasmx86]
_RtlEnterCriticalSection@4

var_C           = dword ptr -0Ch
var_8           = dword ptr -8
var_4           = dword ptr -4
arg_0           = dword ptr  8

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                sub     esp, 0Ch
                push    esi
                push    edi
                mov     edi, [ebp+arg_0]
                lea     esi, [edi+4] ; LockCount
                mov     eax, esi
                lock btr dword ptr [eax], 0
                jnb     wait ; jump if CF=0

loc_7DE922DD:
                mov     eax, large fs:18h
                mov     ecx, [eax+24h]
                mov     [edi+0Ch], ecx
                mov     dword ptr [edi+8], 1
                pop     edi
                xor     eax, eax
                pop     esi
                mov     esp, ebp
                pop     ebp
                retn    4

... skipped
\end{lstlisting}

\myindex{x86!\Instructions!BTR}
\myindex{x86!\Prefixes!LOCK}
Самая важная инструкция в этом фрагменте кода --- это
 \TT{BTR} 
(с префиксом \TT{LOCK}): 
нулевой бит сохраняется в флаге CF и очищается в памяти
.
Это \glslink{atomic operation}{атомарная операция}, 
блокирующая доступ всех остальных процессоров
к этому значению в памяти (обратите внимание на префикс \TT{LOCK} перед инструкцией \TT{BTR}.

Если бит в \TT{LockCount} является 1, 
хорошо, сбросить его и вернуться из функции: мы в критической секции
.
Если нет --- критическая секция уже занята другим тредом, тогда ждем
. \\
Ожидание там сделано через вызов WaitForSingleObject(). \\
\\
А вот как работает функция LeaveCriticalSection():

\begin{lstlisting}[caption=Windows 2008/ntdll.dll/x86 (begin),style=customasmx86]
_RtlLeaveCriticalSection@4 proc near

arg_0           = dword ptr  8

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                push    esi
                mov     esi, [ebp+arg_0]
                add     dword ptr [esi+8], 0FFFFFFFFh ; RecursionCount
                jnz     short loc_7DE922B2
                push    ebx
                push    edi
                lea     edi, [esi+4]    ; LockCount
                mov     dword ptr [esi+0Ch], 0
                mov     ebx, 1
                mov     eax, edi
                lock xadd [eax], ebx
                inc     ebx
                cmp     ebx, 0FFFFFFFFh
                jnz     loc_7DEA8EB7

loc_7DE922B0:
                pop     edi
                pop     ebx

loc_7DE922B2:
                xor     eax, eax
                pop     esi
                pop     ebp
                retn    4

... skipped
\end{lstlisting}

\myindex{x86!\Instructions!XADD}
\TT{XADD} это \q{обменять и прибавить}.
В данном случае, это значит прибавить 1 к значению в \TT{LockCount}, при этом сохранить изначальное значение \TT{LockCount}
в регистре \TT{EBX}.
Впрочем, значение в \TT{EBX} позже инкрементируется при помощи последующей инструкции \INS{INC EBX},
и оно также будет равно обновленному значению \TT{LockCount}.

Эта операция также атомарная, потому что также имеет префикс \TT{LOCK}, что означает, что другие CPU
или ядра CPU в системе не будут иметь доступа к этой ячейке памяти
.

Префикс \TT{LOCK} очень важен: 
два треда, каждый из которых работает на разных CPU или ядрах CPU, могут попытаться одновременно
войти в критическую секцию, одновременно модифицируя значение в памяти, и это может привести к
непредсказуемым результатам.


% TODO linux

